package math;

public class mat2
{
    
    public static final int NUM_ROWS = 2;
    public static final int NUM_COLS = 2;
    
    public static final int NUM_ENTRIES = mat2.NUM_ROWS * mat2.NUM_COLS;
    
    public static final mat2 IDENTITY = new mat2();
    
    public static final mat2 ZERO = new mat2(0.0);
    
    
    /**
    Array containing matrix entries. Index-form:
        0   2
        1   3
    */
    public double[] m;
    
    
    public mat2()
    {
        this(1.0);
    }
    
    public mat2(double aEntry)
    {
        this.m = new double[mat2.NUM_ENTRIES];
        this.m[0] = this.m[3] = aEntry;
    }
    
    
    public final void set(mat2 aOther)
    {
        System.arraycopy(aOther.m, 0, this.m, 0, mat2.NUM_ENTRIES);
    }
    
    
    public void mul(mat2 aOther)
    {
        mat2 mul = mat2.mul(this, aOther);
        this.set(mul);
    }
    
    public void mulEntrywise(mat2 aOther)
    {
        for (int i = 0; i < mat2.NUM_ENTRIES; i++)
        {
            double ithMultiply = this.m[i] * aOther.m[i];
            this.m[i] = ithMultiply;
        }
    }
    
    public void transpose()
    {
        mat2.swap(this, 1, 2); // Swap 1 <-> 2.
    }
    
    
    public vec2 mul(vec2 aVec)
    {
        return new vec2(
            this.m[0] * aVec.x + this.m[2] * aVec.y, 
            this.m[1] * aVec.x + this.m[3] * aVec.y
        );
    }
    
    
    public mat2 copy()
    {
        mat2 copy = new mat2();
        copy.set(this);
        return copy;
    }
    
    
    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        sb.append("mat2\n   ");
        sb.append(this.m[0]);
        sb.append(", ");
        sb.append(this.m[2]);
        sb.append("\n   ");
        sb.append(this.m[1]);
        sb.append(", ");
        sb.append(this.m[3]);
        return sb.toString();
    }
    
    
    
    public static mat2 mul(mat2 aA, mat2 aB)
    {
        mat2 mul = new mat2();
        mat2.mul(aA, aB, mul);
        return mul;
    }
    
    public static mat2 rotate(double aAngleRad)
    {
        double halfAngle = 0.5 * aAngleRad;
        double sinHalfAngle = math.sin(halfAngle);
        double r = sinHalfAngle;
        double s = math.cos(halfAngle);
        mat2 rot = new mat2();
        rot.m[0] = 1 - 2 * r * r;
        rot.m[1] = 2 * s * r;
        rot.m[2] = -2 * s * r;
        rot.m[3] = 1 - 2 * r * r;
        return rot;
    }
    
    public static mat2 scale(vec2 aScale)
    {
        mat2 scale = new mat2();
        scale.m[0] = aScale.x;
        scale.m[3] = aScale.y;
        return scale;
    }
    
    public static mat2 transl(double aTransX)
    {
        mat2 transl = new mat2();
        transl.m[2] = aTransX;
        return transl;
    }
    
    
    
    private static void mul(mat2 aA, mat2 aB, mat2 aDST)
    {
        for (int i = 0; i < mat2.NUM_ENTRIES; i++)
        {
            int imod2 = i % 2;
            int idiv2 = i / 2;
            double dotProduct = 0.0;
            for (int j = 0; j < 2; j++)
            {
                int indA = j * 2 + imod2;
                int indB = j + 2 * idiv2;
                double jthMultiply = aA.m[indA] * aB.m[indB];
                dotProduct += jthMultiply;
            }
            aDST.m[i] = dotProduct;
        }
    }
    
    private static void swap(mat2 aA, int indA, int indB)
    {
        double t = aA.m[indA];
        aA.m[indA] = aA.m[indB];
        aA.m[indB] = t;
    }
    
}
